package com.example.project4;

//import java.io.BufferedWriter;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
//import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.widget.Toast;

public class ConnectionService extends Service{
	
	Song [] serverSongs;
	Song [] clnSongs;
	
    static final int CONNECT = 0, LIST = 1, DIFF = 2, PULL = 3, LEAVE = 4, CAP = 5;
    static final String IP_ADDRESS = "10.0.2.2";   //for lovalhost or "130.207.114.21" for shuttle
    static final int PORT_NUMBER = 2013;
    static final int TITLELEN = 150;
    static final int MD5_DIGEST_LENGTH = 18;
    static final int INT_SIZE = 4;
	private Socket socket = null;
	OutputStream out = null;
    DataOutputStream dos = null;
    InputStream in = null;
    DataInputStream dis = null;
	
    
    /**
     * Handler of incoming messages from bond activity.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LIST:
                	List listReq = new List();
                	listReq.execute();
                    Toast.makeText(getBaseContext(), "After List!", Toast.LENGTH_SHORT).show();
                    break;
                case DIFF:
                	Diff diffReq = new Diff();
                	diffReq.execute();
                    Toast.makeText(getBaseContext(), "After diff!", Toast.LENGTH_SHORT).show();
                    break;
                case PULL:
                	//clnt.execute(3);
                    Toast.makeText(getBaseContext(), "After pull!", Toast.LENGTH_SHORT).show();
                    break;
                case LEAVE:
                	//clnt.execute(4);
                    Toast.makeText(getBaseContext(), "After Leave!", Toast.LENGTH_SHORT).show();
                    break;
                case CAP:
                	//clnt.execute(5);
                    Toast.makeText(getBaseContext(), "After cap!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
    
    /**
     * This handles all the client connections to the server
     * It must be done on the background and NO TOAST can be print here
     */

    private class List extends AsyncTask<Integer, Void, Socket> {
    	
        protected Socket doInBackground(Integer... params) {
        	try {
        		if (socket == null) {
	    			InetAddress serverAddr = InetAddress.getByName(IP_ADDRESS);  
	    			socket = new Socket(serverAddr, PORT_NUMBER);
	    			Log.d("Connection: ", "CONNECTED");
        		}
		    } catch (IOException e) {
		    	Log.e("Connection: ", "Failed to connect...");
		    	e.printStackTrace();
	    	}

	    	Header head = new Header(0,0,0);
	    	try{
	    		//send List Request to server
	    		out = socket.getOutputStream(); 
	    		dos = new DataOutputStream(out);
	    		dos.write(head.getHeaderByteArray());
	            out.flush();
	            //Receive List response with total length for next message and number of songs
	            byte [] headerBA = new byte[INT_SIZE*3];
	            in = socket.getInputStream();
	            dis = new DataInputStream(in);
	            dis.read(headerBA);
	            Header rcvHead = new Header(headerBA);
	            Log.d("Connection: ", "Received: "+rcvHead.toString());
	                    
	            //if total length for next message and number of songs are zero then server has no songs
	            if((rcvHead.getIndexes()==0) && (rcvHead.getLength()==0)){
	            	Log.d("Connection: ","There are no songs on the server.");
	            } else {
	                //else receive all songs info from server
	                int srvNumSongs = rcvHead.getIndexes();
		            serverSongs = new Song[srvNumSongs];
		            int sizeSong = (TITLELEN+MD5_DIGEST_LENGTH+INT_SIZE);
		            byte [] songBA = new byte[sizeSong];
		            int sizeTotalSongs = sizeSong*srvNumSongs; 		             
		            byte [] songsBA = new byte[sizeTotalSongs];
		            in = socket.getInputStream();
		            dis = new DataInputStream(in);
		            dis.read(songsBA);	
		            int i,j;
		            for(i = 0; i<srvNumSongs; i++){
		            	for(j = 0; j < sizeSong ; j++){
		            		songBA[j] = songsBA[j+(sizeSong*i)];
		                }
		                serverSongs [i] = new Song(songBA);
		            }
		            for(i = 0; i<srvNumSongs; i++){
		                Log.d("Connection: ","Server Song "+ i + ": "+serverSongs[i].toString());
		            }
	            } 
	    	}catch (IOException e) {
	    		 Log.e("Connection: ", "Failed to send list request...");
	    	}
	    	return socket;
        }      
    }
    private class Diff extends AsyncTask<Integer, Void, Socket> {
    	SongsManager clientSongManager = new SongsManager();

        protected Socket doInBackground(Integer... params) {
        	//Connect if not already connected
        	try {
        		if (socket == null) {
	    			InetAddress serverAddr = InetAddress.getByName(IP_ADDRESS);  
	    			socket = new Socket(serverAddr, PORT_NUMBER);
	    			Log.d("Connection: ", "CONNECTED");
        		}
		    } catch (IOException e) {
		    	Log.e("Connection: ", "Failed to connect...");
		    	e.printStackTrace();
	    	}
			Log.d("Connection: ", "Diffing");
			Header head = new Header(1,0,0);
	    	try{
	    		//send List Request to server
	    		out = socket.getOutputStream(); 
	    		dos = new DataOutputStream(out);
	    		dos.write(head.getHeaderByteArray());
	            out.flush();
	            //Receive List response with total length for next message and number of songs
	            byte [] headerBA = new byte[INT_SIZE*3];
	            in = socket.getInputStream();
	            dis = new DataInputStream(in);
	            dis.read(headerBA);
	            Header rcvHead = new Header(headerBA);
	            Log.d("Connection: ", "Received: "+rcvHead.toString());
	                    
	            //if total length for next message and number of songs are zero then server has no songs
	            if((rcvHead.getIndexes()==0) && (rcvHead.getLength()==0)){
	            	Log.d("Connection: ","There are no songs on the server.");
	            } else {
	                //else receive all songs info from server
	                int srvNumSongs = rcvHead.getIndexes();
		            serverSongs = new Song[srvNumSongs];
		            int sizeSong = (TITLELEN+MD5_DIGEST_LENGTH+INT_SIZE);
		            byte [] songBA = new byte[sizeSong];
		            int sizeTotalSongs = sizeSong*srvNumSongs; 		             
		            byte [] songsBA = new byte[sizeTotalSongs];
		            in = socket.getInputStream();
		            dis = new DataInputStream(in);
		            dis.read(songsBA);	
		            int i,j;
		            for(i = 0; i<srvNumSongs; i++){
		            	for(j = 0; j < sizeSong ; j++){
		            		songBA[j] = songsBA[j+(sizeSong*i)];
		                }
		                serverSongs [i] = new Song(songBA);
		            }
		            
		            //DIFF STUFF
		            
		            int numOfClnSong = clientSongManager.getNumClienSongs();
		    		Log.d("Connection: ", "Number of Songs in Client: "+ numOfClnSong);
		    		clnSongs = new Song[numOfClnSong];
		    		clnSongs = clientSongManager.getSongs();
		            for(i = 0; i<numOfClnSong; i++){
		            	Log.d("Connection: ","Client Song "+ i + ": "+clnSongs[i].toString());
		            }
		            
		            int maxSongs=(srvNumSongs>numOfClnSong)?srvNumSongs:numOfClnSong;
	            	Log.d("Connection: ","maxSongs "+ maxSongs);

		            Song [] diffSongs = new Song[maxSongs];

		            int s = 0, c= 0, numDiff = 0;
		            boolean b = true;
		            Log.d("Connection: ","srvNumSongs "+ srvNumSongs);
		            Log.d("Connection: ","numOfClnSong "+ numOfClnSong);
		            //compare songs
		            while(srvNumSongs>s){
		            	c=0;
		            	b=true;
		            	while(numOfClnSong>c){	
		            		if(Arrays.equals(serverSongs[s].checksum, clnSongs[c].checksum)){
		            			b=false;
		            		}
		            		c++;
		            	}
		            	if(b){
		            		diffSongs[numDiff] = serverSongs[s];
		            		numDiff++;
		            	}
		            	s++;
		            }
		            Log.d("Connection: ","numDiff "+ numDiff);
		            for(i=0; i<numDiff; i++){
		            	Log.d("Connection: ","Diff Song "+ i + ": "+diffSongs[i].toString());
		            }
		            
	            } 
	    	}catch (IOException e) {
	    		 Log.e("Connection: ", "Failed to send list request in diff...");
	    	}
	    	return socket;
        }      
    }
    
}
